package indi.zhifa.recipe.bailan.framework.auth.filter;

import indi.zhifa.recipe.bailan.framework.auth.entity.annotations.Passwd;
import indi.zhifa.recipe.bailan.framework.auth.entity.annotations.PasswdEncode;
import indi.zhifa.recipe.bailan.framework.auth.entity.annotations.UserName;
import indi.zhifa.recipe.bailan.framework.auth.property.PasswdConfig;
import indi.zhifa.recipe.bailan.framework.auth.property.UserConfig;
import indi.zhifa.recipe.bailan.framework.auth.util.IZfEncryptUtil;
import indi.zhifa.recipe.bailan.framework.common.entity.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.security.SecurityConfig;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;


@Order(103)
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class PasswdEncodeFilters {

    private final UserConfig mUserConfig;
    private final IZfEncryptUtil mZfEncryptUtil;

    @Around("@annotation(passwdEncode)")
    public Object spAdminAop(ProceedingJoinPoint pJoinPoint, PasswdEncode passwdEncode) throws Throwable {
        MethodSignature signature = (MethodSignature)pJoinPoint.getSignature();
        Annotation[][] annotations =  signature.getMethod().getParameterAnnotations();
        Object[] params = pJoinPoint.getArgs();
        String[] paramNames = signature.getParameterNames();
        for(int i=0;i<params.length;i++){
            Object param = params[i];
            Annotation[] paramAnnotations = annotations[i];

            for(Annotation an : paramAnnotations){
                if(an.annotationType().equals(Passwd.class)){
                    if(param instanceof String){
                        String rawPasswd = (String)param;
                        try{
                            params[i] = decodePasswd(rawPasswd);
                        }catch (Exception ex){
                            throw new ServiceException(paramNames[i]+" "+ex.getMessage());
                        }
                    }
                    break;
                }
                else if(an.annotationType().equals(UserName.class)){
                    if(param instanceof String){
                        String rawUserName = (String)param;
                        try{
                            params[i] = decodeUserName(rawUserName);
                        }catch (Exception ex){
                            throw new ServiceException(paramNames[i]+" "+ex.getMessage());
                        }
                    }
                    break;
                }else if(an.annotationType().equals(RequestBody.class)){
                    Field[] fields = param.getClass().getDeclaredFields();
                    for(Field field : fields){
                        handlePasswd(param,field);
                        handleName(param,field);
                    }
                }
            }
        }
        return pJoinPoint.proceed(params);
    }

    protected void handlePasswd(Object pDto, Field pField) throws IllegalAccessException {
        Passwd passwd = pField.getAnnotation(Passwd.class);
        if(null != passwd){
            Class fieldType = pField.getType();
            if(null != fieldType && fieldType == String.class){
                boolean accessible = pField.isAccessible();
                pField.setAccessible(true);
                Object objPsd = pField.get(pDto);
                if(null != objPsd){
                    String psd = (String) objPsd;
                    if(StringUtils.hasText(psd)){
                        String decodePsd = decodePasswd(psd);
                        pField.set(pDto,decodePsd);
                    }
                }
                pField.setAccessible(accessible);
            }
        }
    }

    protected void handleName(Object pDto, Field pField) throws IllegalAccessException {
        UserName userName = pField.getAnnotation(UserName.class);
        if(null != userName){
            Class fieldType = pField.getType();
            if(null != fieldType && fieldType == String.class){
                boolean accessible = pField.isAccessible();
                pField.setAccessible(true);
                Object objUsr = pField.get(pDto);
                if(null != objUsr){
                    String usr = (String) objUsr;
                    if(StringUtils.hasText(usr)){
                        String decodeUsr = decodeUserName(usr);
                        pField.set(pDto,decodeUsr);
                    }
                }
                pField.setAccessible(accessible);
            }
        }
    }


    private String decodePasswd(String pPasswd){
        String originPwd = pPasswd;
        PasswdConfig passwordConfig = mUserConfig.getPasswd();
        if(passwordConfig.isEncrypt()){
            originPwd = mZfEncryptUtil.passwdDecode(pPasswd);
        }
        return originPwd;
    }

    private String decodeUserName(String pUserName){
        String originUserName= pUserName;
        PasswdConfig passwordConfig = mUserConfig.getPasswd();
        if(passwordConfig.isEncrypt()){
            originUserName = mZfEncryptUtil.userNameDecode(pUserName);
        }
        return originUserName;
    }
}
